import { BadRequestException, Inject, Injectable, UnauthorizedException } from '@nestjs/common';
import { JwtService, JwtUser } from 'src/libs/jwt/jwt.service';
import { PrismaService } from 'src/libs/prisma/prisma.service';
import { LoginUserDto } from './dto/login-user.dto';
import { RegisterUserDto } from './dto/register-user.dto';
import { formatDate, generateRandomNumber, generateSnowId, isDef, md5 } from 'src/utils';
import { UpdateUserDto } from './dto/update-user.dto';
import { UpdateUserPasswordDto } from './dto/update-user-password.dto';
import { EmailService } from 'src/libs/email/email.service';
import { RedisService } from 'src/libs/redis/redis.service';
import { UserRoleType } from '@prisma/client';
import { omit } from 'lodash';
import { FollowStockDto } from './dto/follow-stock.dto';
import { ForgetPasswordDto } from './dto/forget-password.dto';

@Injectable()
export class UserService {
  @Inject(JwtService)
  private readonly jwtService: JwtService;

  @Inject(PrismaService)
  private readonly prismaService: PrismaService;

  @Inject(RedisService)
  private readonly redisService: RedisService;

  @Inject(EmailService)
  private readonly emailService: EmailService;

  async getToken(user: JwtUser) {
    return {
      token: await this.jwtService.sign(user),
      refreshToken: await this.jwtService.sign(user, true),
    };
  }

  async refreshToken(refreshToken: string) {
    const user = await this.jwtService.verify(refreshToken);
    return {
      token: await this.jwtService.sign({
        userId: user.userId,
        username: user.username,
        email: user.email,
      }),
    };
  }

  async login(loginUserDto: LoginUserDto) {
    const { userAccount, password } = loginUserDto;
    const LOCK_KEY = `login_lock:${userAccount}`;
    const maxAttempts = 5;
    const lockDuration = 10 * 60; // 10分钟

    // 1 检查是否被锁定
    const isLockCount = await this.redisService.get(LOCK_KEY);
    if (isLockCount && +isLockCount >= maxAttempts) throw new BadRequestException('密码错误次数过多，账户已被锁定10分钟，请十分钟后重试');

    // 2 查询用户
    const foundUsers = await this.prismaService.user.findMany({
      where: {
        OR: [{ username: userAccount }, { email: userAccount }],
      },
    });
    const foundUser = foundUsers[0];

    // 3 用户不存在, 抛出错误
    if (!foundUser) throw new BadRequestException('用户不存在');

    // 4 密码验证
    if (foundUser.password !== md5(password)) {
      const count = await this.redisService.get(LOCK_KEY);
      isDef(count) ? await this.redisService.incr(LOCK_KEY) : await this.redisService.set(LOCK_KEY, 1, lockDuration);
      const restAttempt = maxAttempts - (count ? +count : 0) - 1;
      throw new BadRequestException(
        restAttempt === 0 ? `密码错误次数过多，账户已被锁定10分钟，请十分钟后重试` : `密码不正确，还有${restAttempt}次机会`,
      );
    }

    // 5 登录成功，清除错误记录
    await this.redisService.del(LOCK_KEY);

    // 6 返回 Token
    return this.getToken({
      userId: foundUser.id,
      username: foundUser.username,
      email: foundUser.email || '',
    });
  }

  async register(user: RegisterUserDto) {
    // 1 查询用户的邮箱或者用户名是否已注册
    const foundUsers = await this.prismaService.user.findMany({
      where: {
        OR: [{ email: user.email }, { username: user.username }],
      },
    });
    // 2 用户名/邮箱已被使用, 抛出错误
    if (foundUsers.some((item) => item.email === user.email)) throw new BadRequestException('邮箱已存在');
    if (foundUsers.some((item) => item.username === user.username)) throw new BadRequestException('用户名已存在');
    // 3 验证码不正确, 抛出错误
    const cacheCode = await this.redisService.get(`register_valid_code:${user.email}`);
    if (cacheCode !== user.validCode) throw new BadRequestException('验证码不正确');
    // 4 未注册，注册用户
    const newUser = await this.prismaService.user.create({
      data: {
        id: generateSnowId(),
        email: user.email,
        username: user.username,
        password: md5(user.password),
        points: parseInt(process.env.INIT_POINTS) || 10,
        userRole: {
          connect: { role: UserRoleType.NORMAL },
        },
      },
      select: {
        id: true,
        username: true,
        email: true,
        createTime: true,
        updateTime: true,
        points: true,
        vipStartTime: true,
        vipEndTime: true,
      },
    });

    // 5 返回 Token
    const { token, refreshToken } = await this.getToken({
      userId: newUser.id,
      username: newUser.username,
      email: newUser.email || '',
    });

    return {
      token,
      refreshToken,
      userInfo: {
        role: UserRoleType.NORMAL,
        ...omit(newUser, ['id']),
      },
    };
  }

  /** 发送邮箱 */
  async sendCode(email: string) {
    // 1 生成随机数
    const validCode = generateRandomNumber(6);
    // 2 是否两分钟前发送过验证码
    const cacheSended = await this.redisService.get(`register_valid_code:${email}_send`);
    if (cacheSended) throw new BadRequestException('禁止频繁发送，请两分钟后重试');
    // 3 发送验证码
    await this.emailService.sendMail({
      to: email,
      subject: '注册验证码',
      html: `<p>你的注册验证码是 ${validCode}</p>`,
    });
    // 4 设置缓存
    await this.redisService.set(`register_valid_code:${email}_send`, validCode, 2 * 60);
    await this.redisService.set(`register_valid_code:${email}`, validCode, 10 * 60);
  }

  async getUserInfo(userId: string) {
    const user = await this.prismaService.user.findUnique({
      where: {
        id: userId,
      },
      select: {
        username: true,
        email: true,
        createTime: true,
        updateTime: true,
        points: true,
        vipStartTime: true,
        vipEndTime: true,
        userRole: { select: { role: true } },
      },
    });
    if (!user) throw new UnauthorizedException('用户不存在');
    return { ...omit(user, ['userRole']), role: user?.userRole?.role };
  }

  async getPoints(userId: string) {
    const user = await this.prismaService.user.findUnique({
      where: { id: userId },
      select: { points: true },
    });
    return user?.points || 0;
  }

  async updateUserInfo(userId: string, user: UpdateUserDto) {
    const updatedUser = await this.prismaService.user.update({
      where: { id: userId },
      data: user,
      select: {
        username: true,
        email: true,
        createTime: true,
        updateTime: true,
      },
    });
    return updatedUser;
  }

  async updatePwdCaptcha(address: string) {
    // 1 查询用户
    const foundUser = await this.prismaService.user.findUnique({
      where: { email: address },
    });
    // 2 用户不存在, 抛出错误
    if (!foundUser) throw new BadRequestException('用户不存在');
    // 3 生成验证码
    const code = Math.random().toString().slice(2, 8);
    // 4 设置验证码
    console.log(process.env.REDIS_UPDATE_PWD_PREFIX + address);
    await this.redisService.set(process.env.REDIS_UPDATE_PWD_PREFIX + address, code, 10 * 60);
    await this.emailService.sendMail({
      to: address,
      subject: '更改密码验证码',
      html: `<p>你的更改密码验证码是 ${code}</p>`,
    });
  }

  async updatePassword(passwordDto: UpdateUserPasswordDto) {
    const { password, email, captcha } = passwordDto;
    // 1 查询用户
    const foundUser = await this.prismaService.user.findUnique({
      where: { email },
    });
    // 2 用户不存在, 抛出错误
    if (!foundUser) throw new BadRequestException('用户不存在');
    // 3 验证码不正确, 抛出错误
    const redisCaptcha = await this.redisService.get(process.env.REDIS_UPDATE_PWD_PREFIX + email);
    if (redisCaptcha !== captcha) throw new BadRequestException('验证码不正确');
    // 4 更新密码
    await this.prismaService.user.update({ where: { id: foundUser.id }, data: { password: md5(password) } });
  }

  /** 发送忘记密码验证码 */
  async sendForgetPasswordCode(email: string) {
    // 1 查询用户
    const foundUser = await this.prismaService.user.findUnique({ where: { email } });
    // 2 用户不存在, 抛出错误
    if (!foundUser) throw new BadRequestException('用户不存在');
    // 3 生成随机数
    const validCode = generateRandomNumber(6);
    // 4 是否两分钟前发送过验证码
    const cacheSended = await this.redisService.get(`forget_password_code:${email}_send`);
    if (cacheSended) throw new BadRequestException('禁止频繁发送，请两分钟后重试');
    // 5 发送验证码
    await this.emailService.sendMail({
      to: email,
      subject: '忘记密码验证码',
      html: `<p>你的忘记密码验证码是 ${validCode}</p>`,
    });
    // 6 设置缓存
    await this.redisService.set(`forget_password_code:${email}_send`, validCode, 2 * 60);
    await this.redisService.set(`forget_password_code:${email}`, validCode, 10 * 60);
  }

  /** 忘记密码 */
  async forgetPassword(forgetPasswordDto: ForgetPasswordDto) {
    const { email, captcha, password } = forgetPasswordDto;
    // 1 查询用户
    const foundUser = await this.prismaService.user.findUnique({ where: { email } });
    // 2 用户不存在, 抛出错误
    if (!foundUser) throw new BadRequestException('用户不存在');
    // 3 验证码不正确, 抛出错误
    const redisCaptcha = await this.redisService.get(`forget_password_code:${email}`);
    if (redisCaptcha !== captcha) throw new BadRequestException('验证码不正确');
    // 4 更新密码
    await this.prismaService.user.update({ where: { id: foundUser.id }, data: { password: md5(password) } });
    // 5 删除验证码
    await this.redisService.del(`forget_password_code:${email}`);
  }
}
